package com.gitee.feizns.dynamic;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <b>数组操作工具类</b>
 * @see Array
 * @author feizns
 * @since 2019/5/13
 */
public abstract class ArrayUtils {

    /**
     * <b>查找第一个元素的下标</b>
     * @param test 查找的判断条件
     * @param array 数组
     * @return array中第一个匹配test规则的元素下标
     */
    @SafeVarargs
    public static <T> int findIndex(Predicate<T> test, T... array) {
        if ( array != null ) {
            for (int i = 0; i < array.length; i++) {
                if ( test.test(array[i]) )
                    return i;
            }
        }
        return -1;
    }

    /**
     * <b>查找第一个元素的下标</b>
     * @param test 查找的判断条件
     * @param array 数组
     * @return array中第一个匹配test规则的元素下标
     */
    @SafeVarargs
    public static <T> int indexOf(Predicate<T> test, T... array) {
        return findIndex(test, array);
    }

    /**
     * <b>查找第一个元素</b>
     * @param test 查找的判断条件
     * @param array 数组
     * @return array中第一个匹配test规则的元素
     */
    @SafeVarargs
    public static <T> T findFirst(Predicate<T> test, T... array) {
        int index = findIndex(test, array);
        return index != -1 ? array[index] : null;
    }

    /**
     * <b>将数组包装成{@code Iterator}</b>
     * @see Iterator
     * @param ele 数组元素
     * @param <T> 数组元素类型
     * @return 返回包含ele中所有元素的迭代器
     */
    @SafeVarargs
    public static <T> Iterator<T> iterator(T... ele) {
        return ele != null ? Arrays.stream(ele).iterator() : Collections.emptyIterator();
    }

    /**
     * <b>将数组包装成{@code Iterable}</b>
     * @see Iterable
     * @param ele 数组元素
     * @param <T> 数组元素类型
     * @return 返回包含ele中所有元素的迭代器
     */
    @SafeVarargs
    public static <T> Iterable<T> iterable(T... ele) {
        return () -> iterator(ele);
    }

    /**
     * <b>判断数组是否为空</b>
     * @param ele 数组
     * @param <T> 数组类型
     * @return 数组为空返回true不为空返回false
     */
    @SafeVarargs
    public static <T> boolean isEmpty(T... ele) {
        return ele == null || ele.length == 0;
    }

    /**
     * <b>判断数组是否非空</b>
     * @param ele 数组元素
     * @param <T> 数组元素类型
     * @return 数组为空返回false不为空返回true
     */
    @SafeVarargs
    public static <T> boolean isNotEmpty(T... ele) {
        return !isEmpty(ele);
    }

    /**
     * <b>遍历一个未知类型的元素</b>
     * <pre>{@code
     *  Object array = new Integer[]{1, 2, 3};
     *  ArrayUtils.forEach(array, System.out::print);//123
     * }</pre>
     * @param array 可能是数组的对象
     * @param consumer 消费数组元素
     */
    public static void forEach(Object array, Consumer<Object> consumer) {
        if ( array != null && array.getClass().isArray() ) {
            for (int i = 0; i < Array.getLength(array); i++) {
                consumer.accept(Array.get(array, i));
            }
        }
    }

    /**
     * <b>尝试将一个类型未知的元素构建成流</b>
     * <pre>{@code
     *  //例：
     *  Object array = new Integer[]{1, 2, 3};
     *  String[] strings = stream(array).map(Object::toString).toArray(String[]::new);
     * }</pre>
     * @param array 可能是数组的对象
     * @return 转载array中所有元素的流
     */
    public static Stream<Object> stream(Object array) {
        Stream.Builder<Object> builder = Stream.builder();
        if ( array != null && array.getClass().isArray() ) {
            for (int i = 0; i < Array.getLength(array); i++) {
                builder.add(Array.get(array, i));
            }
        }
        return builder.build();
    }

    /**
     * 将单元素的集合和数组解析单对象
     * @param array 数组
     * @return {@link Object}
     */
    public static Object unpack(Object array) {
        if ( array != null ) {
            Class<?> type = array.getClass();
            if ( type.isArray()
                    && Array.getLength(array) == 1 )
                return Array.get(array, 0);
            else if ( Collection.class.isAssignableFrom(type)
                    && ((Collection<?>) array).size() == 1 ) {
                return IteratorUtils.first(((Collection<?>) array).iterator());
            }
        }
        return array;
    }

    /**
     * <b>将未知对象装换为数组</b>
     * @param arr 数组
     * @param <T> 数组类型
     * @return 返回指定元素的数组
     */
    @SuppressWarnings("unchecked")
    public static <T> T[] toArray(Object arr) {
        return arr != null ? (T[]) toArray(arr, Object[]::new) : null;
    }

    /**
     * <b>将未知的对象封装成功数组</b>
     * @param arr 未知对象
     * @param supplier 数组提供者
     * @param <T> 数组元素类型
     * @return 返回对应的数组
     */
    @SuppressWarnings("unchecked")
    public static <T> T[] toArray(Object arr, IntFunction<T[]> supplier) {
        if ( arr != null ) {
            Class<?> aClass = arr.getClass();
            if ( aClass.isArray() ) {
                T[] ret = supplier.apply(Array.getLength(arr));
                for (int i = 0; i < Array.getLength(arr); i++)
                    ret[i] = (T) Array.get(arr, i);
                return ret;
            } else if ( Collection.class.isAssignableFrom(aClass) ) {
                Collection<T> tmp = (Collection<T>) arr;
                return tmp.toArray(supplier.apply(tmp.size()));
            } else if ( Iterator.class.isAssignableFrom(aClass) ) {
                List<T> list = IteratorUtils.toList((Iterator<T>) arr);
                return list.toArray(supplier.apply(list.size()));
            } else {
                T[] ts = supplier.apply(1);
                ts[0] = (T) arr;
                return ts;
            }
        }
        return null;
    }

    /**
     * <b>将数组转换成用逗号分隔的字符串</b>
     * <pre>{@code
     *  //例：
     *  System.out.println(toString("A", "B", "C"));//"A,B,C"
     * }</pre>
     * @param array 数组
     * @param <T> 数组类型
     * @return [1,2,3] => "1,2,3"
     */
    @SafeVarargs
    public static <T> String toString(T... array) {
        return toString(",", array);
    }

    /**
     * <b>将数组转换成用逗号分隔的字符串</b>
     * <pre>{@code
     *  //例：
     *  System.out.println(toString("=", "A", "B", "C"));//A=B=C
     * }</pre>
     * @param delimiter 分隔符
     * @param array 数组
     * @param <T> 数组类型
     * @return [1,2,3] if delimiter = "?" => "1?2?3"
     */
    @SafeVarargs
    public static <T> String toString(String delimiter, T... array) {
        return  array != null
                ? Arrays.stream(array)
                .map(Objects::toString)
                .collect(Collectors.joining(delimiter))
                : null;
    }

    /**
     * <b>数组强转</b>
     * <pre>{@code
     *  //例：
     *  Object[] array = new String[]{"A", "B", "C"};
     *  String[] stringArray = cast(array, String[]::new);
     *  System.out.println(Arrays.toString(stringArray));//["A", "B", "C"]
     * }</pre>
     * @param array 数组
     * @param generator 数组构建器
     * @param <T> 数组构建器类型（数组目标类型）
     * @return 构建器元素类型的数组
     */
    @SuppressWarnings("unchecked")
    public static <T> T[] cast(Object[] array, IntFunction<T[]> generator) {
        return array != null ? Arrays.stream(array).map(e -> (T) e).toArray(generator) : null;
    }

}
